//#include <string.h>
//#include <stddef.h>

#include "mible_api.h"
#include "common/crc32.h"
#include "cryptography/mi_crypto_backend_uECC.h"

#undef  MI_LOG_MODULE_NAME
#define MI_LOG_MODULE_NAME "POTP"
#include "mible_log.h"

#if (MI_MESH_ENABLED==1)
#include "common/crc32.h"
#include "cryptography/mi_crypto.h"
#include "cryptography/mi_mesh_otp.h"
#include "cryptography/mi_mesh_otp_config.h"

#define CEIL_DIV(A, B)           (((A) + (B) - 1) / (B))

/*   Pseudo OTP   */
#define CRC32_SEED              0xD75C3F0DUL
#define NORMAL_EXIT             3
#define MAX_RETRY_TIMES         3

typedef struct {
    char        name[4];
    uint16_t    version;
    uint16_t    size;
} otp_head_t;

typedef struct {
    uint16_t            type;
    uint16_t            len;
    uint8_t             value[1];
} otp_item_t;

static uart_sync_tx_t uart_send;
static uart_sync_rx_t uart_recv;
static dbg_flash_lock_func_t lock_flash;
static dbg_flash_lock_func_t lock_debug;

static uint8_t * m_otp_base = POTP_BASE;

#if (HAVE_MSC==0)
static const uint8_t root_cert[]={
0x30,0x82,0x01,0x6B,0x30,0x82,0x01,0x0F,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x03,
0xF5,0x0A,0x60,0x30,0x0C,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,0x05,
0x00,0x30,0x22,0x31,0x13,0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x4D,0x69,
0x6A,0x69,0x61,0x20,0x52,0x6F,0x6F,0x74,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,
0x06,0x13,0x02,0x43,0x4E,0x30,0x20,0x17,0x0D,0x31,0x36,0x31,0x31,0x32,0x33,0x30,
0x31,0x33,0x39,0x34,0x35,0x5A,0x18,0x0F,0x32,0x30,0x36,0x36,0x31,0x31,0x31,0x31,
0x30,0x31,0x33,0x39,0x34,0x35,0x5A,0x30,0x22,0x31,0x13,0x30,0x11,0x06,0x03,0x55,
0x04,0x0A,0x13,0x0A,0x4D,0x69,0x6A,0x69,0x61,0x20,0x52,0x6F,0x6F,0x74,0x31,0x0B,
0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x4E,0x30,0x59,0x30,0x13,0x06,
0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x03,
0x01,0x07,0x03,0x42,0x00,0x04,0xBE,0xF5,0x8B,0x02,0xDA,0xE3,0xFF,0xF8,0x54,0x1A,
0xA0,0x44,0x8F,0xBA,0xC4,0x4D,0xB7,0xC6,0x9A,0x2F,0xA8,0xF0,0xB1,0xB6,0xFF,0x7A,
0xD9,0x51,0xDB,0x66,0x28,0xFA,0xD7,0xF0,0x20,0xEA,0x39,0xA2,0xEE,0x86,0x7F,0xDD,
0x78,0x3F,0xDC,0x2F,0xB0,0x86,0x09,0x5C,0xC2,0x85,0x04,0x13,0xA2,0x80,0x2C,0x62,
0x7D,0xBD,0xC7,0x15,0xF4,0xF9,0xA3,0x2F,0x30,0x2D,0x30,0x0C,0x06,0x03,0x55,0x1D,
0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,
0x16,0x04,0x14,0x96,0xB7,0xA2,0x7C,0x39,0xB1,0xB9,0x66,0x33,0xA9,0xF8,0xD1,0x09,
0xB2,0x00,0x60,0xC8,0xE6,0xC5,0x11,0x30,0x0C,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,
0x04,0x03,0x02,0x05,0x00,0x03,0x48,0x00,0x30,0x45,0x02,0x20,0x72,0x17,0x22,0x2B,
0xD8,0x7A,0xB5,0x99,0x9F,0xAC,0xFF,0x0A,0x6B,0xA2,0xB3,0x89,0x0E,0x0B,0xBF,0x78,
0x4A,0x7E,0xB1,0xED,0x01,0x95,0xCC,0x1C,0xF7,0x5A,0xE0,0x86,0x02,0x21,0x00,0x86,
0x6B,0x3F,0x77,0x82,0x01,0x5C,0x6E,0xB1,0xBD,0xBB,0x6C,0x22,0xC6,0x3C,0x75,0x1C,
0xA5,0xB8,0x0D,0x40,0x39,0x35,0x76,0x46,0xD3,0x05,0x90,0x4E,0x92,0x53,0x6D,
};
int cpy_root_cert(uint8_t *p_root_cert,uint16_t root_cert_size)
{
	#if (MI_CER_MODE == DEMO_CER_MODE)
	if(root_cert_size>sizeof(root_cert)){
		root_cert_size = sizeof(root_cert);
	}
	memcpy(p_root_cert,root_cert,root_cert_size);
	return root_cert_size;
	#elif(MI_CER_MODE == FLASH_CER_MODE)
	uint16_t buf_len;
	buf_len = mi_mesh_otp_read(OTP_ROOT_CERT, p_root_cert, root_cert_size);
	return buf_len;
	#endif
}
static const uint8_t manu_cert[] = {
0x30,0x82,0x01,0x8E,0x30,0x82,0x01,0x34,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x01,
0x30,0x0A,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,0x30,0x22,0x31,0x13,
0x30,0x11,0x06,0x03,0x55,0x04,0x0A,0x13,0x0A,0x4D,0x69,0x6A,0x69,0x61,0x20,0x52,
0x6F,0x6F,0x74,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x4E,
0x30,0x20,0x17,0x0D,0x31,0x37,0x30,0x38,0x30,0x39,0x31,0x32,0x30,0x32,0x34,0x32,
0x5A,0x18,0x0F,0x32,0x30,0x36,0x37,0x30,0x37,0x32,0x38,0x31,0x32,0x30,0x32,0x34,
0x32,0x5A,0x30,0x2B,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x0C,0x13,0x4D,
0x69,0x6A,0x69,0x61,0x20,0x4D,0x61,0x6E,0x75,0x66,0x61,0x63,0x74,0x75,0x72,0x65,
0x20,0x32,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x4E,0x30,
0x59,0x30,0x13,0x06,0x07,0x2A,0x86,0x48,0xCE,0x3D,0x02,0x01,0x06,0x08,0x2A,0x86,
0x48,0xCE,0x3D,0x03,0x01,0x07,0x03,0x42,0x00,0x04,0x50,0xCB,0x0D,0x5B,0x99,0x11,
0x80,0xDD,0xEA,0xA5,0x90,0x6E,0x7D,0x62,0xAF,0x98,0xEA,0xC9,0xCE,0x95,0x61,0xF1,
0x75,0x52,0x1C,0xE9,0x3C,0x7C,0x78,0x6B,0x9A,0x8B,0x86,0x94,0x9A,0x3A,0x76,0x33,
0xC8,0x26,0x01,0xBB,0x9E,0x72,0x3C,0xF0,0x51,0x8C,0xEA,0xCE,0xA4,0xFD,0xD7,0x55,
0x6E,0xD1,0x66,0x83,0x77,0x5A,0x19,0x42,0x55,0x03,0xA3,0x50,0x30,0x4E,0x30,0x1F,
0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x96,0xB7,0xA2,0x7C,0x39,
0xB1,0xB9,0x66,0x33,0xA9,0xF8,0xD1,0x09,0xB2,0x00,0x60,0xC8,0xE6,0xC5,0x11,0x30,
0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0xFD,0xEF,0xAB,0x24,0x54,0x64,
0xBA,0x88,0xBD,0x04,0x90,0xAF,0xBB,0x5B,0x4B,0x07,0x8F,0x7B,0x49,0xB7,0x30,0x0C,
0x06,0x03,0x55,0x1D,0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x0A,0x06,0x08,
0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,0x03,0x48,0x00,0x30,0x45,0x02,0x21,0x00,
0xEB,0x14,0x0C,0x5A,0x0E,0xAB,0x61,0x10,0x7D,0xD6,0x98,0x8A,0xF0,0xEB,0x61,0xFC,
0x5F,0xD0,0x9A,0x0F,0xED,0xC9,0x99,0x24,0x96,0x93,0x49,0xEF,0xE0,0x0E,0xAA,0x0A,
0x02,0x20,0x69,0x09,0x65,0xF8,0x1B,0x9A,0x1D,0x49,0x33,0xB9,0x6F,0xEB,0xFA,0x12,
0x5E,0xE3,0x0D,0xC3,0xB6,0x13,0xA1,0xB0,0x53,0x39,0xFC,0x1A,0xE7,0x87,0x74,0xA4,
0xB3,0x82,
};
static const uint8_t dev_cert[] = {
	0x30,0x82,0x01,0x99,0x30,0x82,0x01,0x3E,0xA0,0x03,0x02,0x01,0x02,0x02,0x04,0x3C,
	0xCB,0xF7,0x0D,0x30,0x0C,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,0x03,0x02,0x05,
	0x00,0x30,0x2B,0x31,0x1C,0x30,0x1A,0x06,0x03,0x55,0x04,0x0A,0x13,0x13,0x4D,0x69,
	0x6A,0x69,0x61,0x20,0x4D,0x61,0x6E,0x75,0x66,0x61,0x63,0x74,0x75,0x72,0x65,0x20,
	0x32,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x43,0x4E,0x30,0x20,
	0x17,0x0D,0x31,0x37,0x30,0x38,0x30,0x39,0x31,0x37,0x34,0x39,0x30,0x30,0x5A,0x18,
	0x0F,0x32,0x30,0x36,0x37,0x30,0x37,0x32,0x38,0x31,0x37,0x34,0x39,0x30,0x30,0x5A,
	0x30,0x35,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0A,0x13,0x0C,0x4D,0x69,0x6A,
	0x69,0x61,0x20,0x44,0x65,0x76,0x69,0x63,0x65,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,
	0x04,0x06,0x13,0x02,0x43,0x4E,0x31,0x0F,0x30,0x0D,0x06,0x03,0x55,0x04,0x0B,0x13,
	0x06,0x66,0x74,0x73,0x61,0x66,0x65,0x30,0x59,0x30,0x13,0x06,0x07,0x2A,0x86,0x48,
	0xCE,0x3D,0x02,0x01,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07,0x03,0x42,
	0x00,0x04,0xBE,0x6F,0xC6,0x16,0xA6,0x1A,0x52,0x90,0xB0,0x6B,0x6E,0x7D,0x35,0x31,
	0xF4,0xA4,0x12,0xFE,0xEE,0x17,0x23,0x3A,0x41,0x91,0x2F,0x03,0x5B,0xFF,0x84,0x7E,
	0x3D,0xB3,0xF4,0xEE,0x95,0x95,0xBA,0x06,0xB9,0x4C,0x78,0xE6,0xAB,0x8B,0xE7,0x87,
	0xAD,0xE8,0xFA,0x06,0x36,0xDF,0x2E,0x05,0x73,0x36,0x19,0x54,0x73,0xDD,0xAE,0xF9,
	0xA4,0x20,0xA3,0x42,0x30,0x40,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,
	0x14,0xC0,0x03,0x3D,0x0D,0xA9,0x9A,0x23,0xD1,0xCA,0xE0,0x21,0xCF,0x10,0x32,0x2A,
	0x14,0x02,0xD8,0x77,0xB7,0x30,0x1F,0x06,0x03,0x55,0x1D,0x23,0x04,0x18,0x30,0x16,
	0x80,0x14,0xFD,0xEF,0xAB,0x24,0x54,0x64,0xBA,0x88,0xBD,0x04,0x90,0xAF,0xBB,0x5B,
	0x4B,0x07,0x8F,0x7B,0x49,0xB7,0x30,0x0C,0x06,0x08,0x2A,0x86,0x48,0xCE,0x3D,0x04,
	0x03,0x02,0x05,0x00,0x03,0x47,0x00,0x30,0x44,0x02,0x20,0x4D,0xE5,0xF2,0x36,0x9F,
	0xA4,0x35,0xE4,0x18,0xAB,0x9B,0xA3,0x52,0x54,0x84,0x62,0x6E,0x90,0x68,0x43,0x29,
	0x52,0x70,0x09,0x20,0x3E,0xE2,0x71,0xF3,0xA8,0x03,0x44,0x02,0x20,0x7E,0xAD,0xBE,
	0xAE,0x4B,0xD1,0x10,0xA7,0xD8,0xFF,0x32,0x5B,0x48,0x80,0xEB,0x36,0x71,0x3B,0xCB,
	0xCD,0xC4,0xDD,0x5A,0xCA,0x14,0x41,0x9D,0x1B,0xB7,0x21,0x65,0x3F,

};
static const __ALIGN(4) ecc256_sk_t dev_cert_sk = {
	0x0D,0xEA,0xC3,0x4E,0x78,0x93,0xF9,0xE3,0x1E,0x38,0x12,0x55,0x56,0x92,0x46,0xFB,
	0x04,0x66,0x0B,0x6E,0x1D,0x24,0x4D,0xB2,0xDE,0x62,0x1E,0x0E,0xD8,0xFD,0x83,0x7B,
};

#endif

static void keygen(uint8_t k[16])
{
    memset(k, 0, 16);
    mible_gap_address_get(k);
    mible_aes128_encrypt(k, k, 16, k);
}

static uint8_t *find_item(uint8_t *p_base, uint16_t item_type)
{
	otp_head_t head_str;
	mible_nvm_read((void *)(&head_str),sizeof(head_str),(uint32_t)p_base);
	//otp_head_t *head = (void*)p_base;
	if (memcmp(head_str.name, "POTP", 4) != 0) {
		MI_LOG_ERROR("no mesh otp found.\n");
		return NULL;
	} else if (head_str.version > 1) {
		MI_LOG_ERROR("this version is not supported.\n");
		return NULL;
	}
	otp_item_t item_str;
	mible_nvm_read((void *)(&item_str),sizeof(otp_item_t),((uint32_t)p_base) + sizeof(otp_head_t));
	otp_item_t *p_item = (otp_item_t *)((uint8_t*)p_base + sizeof(otp_head_t));
	//	  MI_LOG_DEBUG("p: type %X len %d\n", p_item->type, p_item->len);
	
	while(item_str.type != item_type && (uint32_t)p_item < POTP_BASE+POTP_FULL_SIZE) {
		/* 4 = 2 byte type + 2 byte lenght */
		p_item = (otp_item_t*)((char*)p_item + 4 + CEIL_DIV(p_item->len, 4) * 4);
		mible_nvm_read((void *)(&item_str),sizeof(otp_item_t),(uint32_t)p_item);
//		  MI_LOG_DEBUG("p %X\n", p_item);
	}

    if ((uint32_t)p_item > POTP_BASE + POTP_FULL_SIZE)
        return NULL;
    else
        return p_item;
}
#define CERTIFY_MAX_LEN 512
int mi_mesh_otp_seal_tag(uint8_t * base)
{
    mible_nvm_init();
    m_otp_base = base;
    otp_head_t head = {
            .name       = "POTP",
            .version    = 1,
            .size       = POTP_FULL_SIZE
    };
    return mible_nvm_write(&head, sizeof(head), (uint32_t)base);
}

int mi_mesh_otp_write(uint16_t item_type, const uint8_t *p_in, uint16_t in_len)
{
    otp_item_t *p = find_item(m_otp_base, OTP_FREE_ITEM);
    if (p == NULL)
        return -1;

    /* 4 = 2 byte type + 2 byte lenght */
    uint16_t len = 4 + CEIL_DIV(in_len, 4) * 4;

    if ((uint32_t)p + len > (uint32_t)m_otp_base + POTP_FULL_SIZE)
        return -2;
    
    uint8_t tmp[CERTIFY_MAX_LEN];
    tmp[0] = item_type;
    tmp[1] = item_type >> 8;
    tmp[2] = in_len;
    tmp[3] = in_len >> 8;

    memcpy(tmp+4, p_in, in_len);
    return mible_nvm_write(tmp, len, (uint32_t)p);
}

int mi_mesh_otp_read(uint16_t item_type, uint8_t *p_out, uint16_t len)
{
    uint8_t *p = find_item(m_otp_base, item_type);

    if (p == NULL)
        return -1;

    otp_item_t item;
    mible_nvm_read(&item, sizeof(otp_item_t), (uint32_t)p);

    if (p_out != NULL && item.len > len)
        return -2;

    if (p_out != NULL) {
        p += offsetof(otp_item_t, value);
        mible_nvm_read(p_out, item.len, (uint32_t)p);
    }

    return item.len;

}

bool mi_mesh_otp_is_existed(void)
{
    static otp_head_t head_str;
	otp_head_t* head;
	mible_nvm_read((void *)(&head_str),sizeof(head_str),POTP_BASE);
	head = &head_str;
    if( memcmp(head->name, "POTP", 4) == 0 &&
        head->version == 1 &&
        head->size == POTP_FULL_SIZE)
        return true;
    else
        return false;
}

int mi_mesh_otp_program_simulation()
{
#if (HAVE_MSC==0)
		uint32_t errno;
		uint8_t key[16];
		uint8_t enc_sk[32+4];
		//need to write the dev sk first 
		uint8_t dev_cert_sk_ram[32];
		memcpy(dev_cert_sk_ram,(void *)dev_cert_sk,sizeof(dev_cert_sk));
		mible_nvm_write(dev_cert_sk_ram,sizeof(dev_cert_sk_ram),DEV_SK_FLASH_ADR);
		if (!mi_mesh_otp_is_existed()) {
			MI_LOG_ERROR("program mesh otp\n");
			mi_mesh_otp_seal_tag((uint32_t *)POTP_BASE);
	
			errno = mi_mesh_otp_write(OTP_ROOT_CERT, root_cert, sizeof(root_cert));
			MI_ERR_CHECK(errno);
	
			errno = mi_mesh_otp_write(OTP_MANU_CERT, manu_cert, sizeof(manu_cert));
			MI_ERR_CHECK(errno);
	
			errno = mi_mesh_otp_write(OTP_DEV_CERT, dev_cert, sizeof(dev_cert));
			MI_ERR_CHECK(errno);
	
			keygen(key);
			mi_crypto_ccm_encrypt(key, key+8, 8, NULL, 0, dev_cert_sk, 32, enc_sk, enc_sk + 32, 4);
			errno = mi_mesh_otp_write(OTP_DEV_CERT_PRI, enc_sk, sizeof(enc_sk));
			MI_ERR_CHECK(errno);
		}
	
		return mi_mesh_otp_verify();
#else
    return 0;
#endif
}


static int send_device_data(uint8_t type, uint8_t const * const out, uint16_t olen)
{
    uint32_t crc32;
    uint8_t cmd[4];
    uint32_t rsp;
    int retry_times = 0;
    /* fill data+crc to send buffer */
    cmd[0] = 0x10;
    cmd[1] = type;
    cmd[2] = (olen + sizeof(crc32)) >> 8;
    cmd[3] = (olen + sizeof(crc32)) & 0xFF;

    uart_send(cmd, 4);
    uart_recv((uint8_t*)&rsp, 4);

    if (rsp != MI_SUCCESS)
        return -1;

    uint8_t buffer[olen+sizeof(crc32)];
    memcpy(buffer, out, olen);
    crc32 = soft_crc32((uint8_t const *)out, (uint32_t)olen, CRC32_SEED);
    memcpy(buffer + olen, &crc32, sizeof(crc32));

    do {
        uart_send(buffer, olen + sizeof(crc32));
        uart_recv((uint8_t*)&rsp, 4);
        if (rsp != MI_SUCCESS)
            retry_times++;
    } while (rsp != MI_SUCCESS && retry_times < MAX_RETRY_TIMES);

    return rsp == MI_SUCCESS ? 0 : -2;
}

static int recv_server_data(uint8_t type, uint8_t * const in, uint16_t max_ilen)
{
    uint16_t rx_len;
    uint32_t crc32;

    uart_recv(in, 4);
    rx_len = (in[2] << 8) | (in[3]);

    if ( type != in[1] )
        return -1;
    else if ( rx_len > max_ilen)
        return -2;

    uart_recv(in, rx_len);
    crc32 = (in[rx_len - 1] << 24) |
            (in[rx_len - 2] << 16) |
            (in[rx_len - 3] << 8)  |
            (in[rx_len - 4]);

    if (crc32 != soft_crc32(in, rx_len - sizeof(crc32), CRC32_SEED)) {
        return -3;
    } else {
        return rx_len - sizeof(crc32);
    }

}

int mi_mesh_otp_manufacture_init(uart_sync_tx_t tx, uart_sync_rx_t rx,
        dbg_flash_lock_func_t lock_flash_fn, dbg_flash_lock_func_t lock_debug_fn)
{
    if( tx == NULL || rx == NULL)
        return -1;

    uart_send = tx;
    uart_recv = rx;

    if( lock_flash_fn != NULL)
        lock_flash = lock_flash_fn;

    if( lock_debug_fn != NULL)
        lock_debug = lock_debug_fn;

    return 0;
}

int mi_mesh_otp_program()
{
    uint32_t errno;
    ecc256_sk_t sk;
    ecc256_pk_t pk;
    uint8_t key[16];
    uint8_t buffer[1024];
    int data_length;
    int retry;
    int response;
    if (mi_mesh_otp_is_existed())
        return 1;

    /* generate dev cert secrect and public key */
    micro_ecc_init(NULL);
    errno = micro_ecc_keypair_gen(NULL, sk, pk);
    if (errno)
        return 2;

    /* UART send public key */
    errno = send_device_data(0, pk, sizeof(pk));
    if (errno != MI_SUCCESS)
        return 3;

    mi_mesh_otp_seal_tag(POTP_BASE);

    /* UART recive mijia root certificate then program it in POTP */
    for (retry = 0; retry < MAX_RETRY_TIMES; retry++) {
        data_length = recv_server_data(0x02, buffer, sizeof(buffer));
        if (data_length > 0) {
            mi_mesh_otp_write(OTP_ROOT_CERT, buffer, data_length);
            response = MI_SUCCESS;
            uart_send((uint8_t const *)&response, 4);
            break;
        } else {
            response = 0x1000000;
            uart_send((uint8_t const *)&response, 4);
        }
    }
    if (retry == MAX_RETRY_TIMES) return 4;

    /* UART recive mijia manufacture certificate then program it in POTP */
    for (retry = 0; retry < MAX_RETRY_TIMES; retry++) {
        data_length = recv_server_data(0x03, buffer, sizeof(buffer));
        if (data_length > 0) {
            mi_mesh_otp_write(OTP_MANU_CERT, buffer, data_length);
            response = MI_SUCCESS;
            uart_send((uint8_t const *)&response, 4);
            break;
        } else {
            response = 0x1000000;
            uart_send((uint8_t const *)&response, 4);
        }
    }
    if (retry == MAX_RETRY_TIMES) return 5;

    /* UART recive mijia device certificate then program it in POTP */
    for (retry = 0; retry < MAX_RETRY_TIMES; retry++) {
        data_length = recv_server_data(0x04, buffer, sizeof(buffer));
        if (data_length > 0) {
            mi_mesh_otp_write(OTP_DEV_CERT, buffer, data_length);
            response = MI_SUCCESS;
            uart_send((uint8_t const *)&response, 4);
            break;
        } else {
            response = 0x1000000;
            uart_send((uint8_t const *)&response, 4);
        }
    }
    if (retry == MAX_RETRY_TIMES) return 6;

    /* UART recive 0x00 00 00 00 00 00 00 00 to exit the certificates program procedure. */
    uart_recv(buffer, 8);

    // encrypt and store the private key.
    keygen(key);
    mi_crypto_ccm_encrypt(key, key + 8, 8, NULL, 0, sk, 32, buffer,
            buffer + 32, 4);
    mi_mesh_otp_write(OTP_DEV_CERT_PRI, buffer, 36);

    errno = mi_mesh_otp_verify();
    if (errno == MI_SUCCESS) {
        if (lock_flash != NULL)
            lock_flash();
        if (lock_debug != NULL)
            lock_debug();
    } else {
        errno  += 0x10;
    }

    return errno;
}

int mi_mesh_otp_verify()
{
    uint32_t errno;
    int16_t buf_len;
    uint8_t buf[1024];

    ecc256_pk_t parent_pub;
    uint8_t     child_sig[64];
    uint8_t     child_sha[32];
    msc_crt_t   crt;

    /* Verify POTP certificates */
    errno = mi_crypto_init();
    if(errno != MI_SUCCESS)
        return 1;

    /* Verify Root cert */
    buf_len = mi_mesh_otp_read(OTP_ROOT_CERT, buf, sizeof(buf));
    errno = mi_crypto_crt_parse_der(buf, buf_len, NULL, &crt);
    if(errno != MI_SUCCESS)
        return 2;

    memcpy(parent_pub, crt.pub.p, 64);

    /* Verify Manu cert */
    buf_len = mi_mesh_otp_read(OTP_MANU_CERT, buf, sizeof(buf));
    errno = mi_crypto_crt_parse_der(buf, buf_len, NULL, &crt);
    if(errno != MI_SUCCESS)
        return 3;

    memcpy(child_sig, crt.sig, 64);

    mi_crypto_sha256(crt.tbs.p, crt.tbs.len, child_sha);
    errno = mi_crypto_ecc_verify(P256R1, parent_pub, child_sha, child_sig);
    if(errno != NORMAL_EXIT)
        return 4;

    memcpy(parent_pub, crt.pub.p, 64);

    /* Verify Device cert */
    buf_len = mi_mesh_otp_read(OTP_DEV_CERT, buf, sizeof(buf));
    mi_crypto_crt_parse_der(buf, buf_len, NULL, &crt);

    memcpy(child_sig, crt.sig, 64);

    mi_crypto_sha256(crt.tbs.p, crt.tbs.len, child_sha);
    errno = mi_crypto_ecc_verify(P256R1, parent_pub, child_sha, child_sig);
    if(errno != NORMAL_EXIT)
        return 5;

    memcpy(parent_pub, crt.pub.p, 64);

    /* Verify Device Private key */
    mi_crypto_ecc_sign(P256R1, child_sha, child_sig);
    if(errno != NORMAL_EXIT)
        return 6;

    errno = mi_crypto_ecc_verify(P256R1, parent_pub, child_sha, child_sig);
    if(errno != NORMAL_EXIT)
        return 7;

    return 0;
}

#endif // MI_MESH_ENABLED==1
